namespace SimpleWebServices.Data
{
    using System;
    using System.Xml;
    using System.Xml.Serialization;
    using SimpleWebServices;
    using Xunit;

    public sealed class LinkFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<Link<IRecordDummy>>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .Implements<IXmlSerializable>()
                .XmlRoot("link")
                .Result);
        }

        [Fact]
        public void deserialize()
        {
            Link<TestRecord> obj = "<link urn='urn://example/test' />".Deserialize<Link<TestRecord>>();

            Assert.Equal<AbsoluteUri>("urn://example/test", obj.Urn);
        }

        [Fact]
        public void deserializeEmpty()
        {
            Assert.Null("<link/>".Deserialize<Link<TestRecord>>().Urn);
        }

        [Fact]
        public void serialize()
        {
            Link<TestRecord> obj = new Link<TestRecord>
            {
                Urn = "urn://example/test"
            };

            Assert.True((bool)obj.Serialize().CreateNavigator().Evaluate("1=count(/link[@urn='urn://example/test'])"));
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new Link<TestRecord>());
        }

        [Fact]
        public void ctor_AbsoluteUri()
        {
            Assert.NotNull(new Link<TestRecord>("urn://test/123"));
        }

        [Fact]
        public void ctor_AbsoluteUriNull()
        {
            Assert.NotNull(new Link<TestRecord>(null as AbsoluteUri));
        }

        [Fact]
        public void prop_Urn()
        {
            Assert.True(new PropertyExpectations(typeof(Link<TestRecord>).GetProperty("Urn"))
                .IsAutoProperty<AbsoluteUri>()
                .Result);
        }

        [Fact]
        public void prop_Get()
        {
            Assert.True(new PropertyExpectations(typeof(Link<TestRecord>).GetProperty("Get"))
                .TypeIs<TestRecord>()
                .Result);
        }

        [Fact]
        public void prop_Get_get()
        {
            Assert.Null(new Link<TestRecord>().Get);
        }

        [Fact]
        public void prop_Get_getUrn()
        {
            FakeRepository.Clear();

            try
            {
                string urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>(new { urn = urn, xml = "<test />" });

                Link<TestRecord> obj = new Link<TestRecord>();

                TestRecord expected = new TestRecord
                {
                    Urn = urn
                };

                obj.Set(expected);

                TestRecord actual = obj.Get;

                Assert.Equal<AbsoluteUri>(expected.Urn, actual.Urn);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void opEquality_LinkT_LinkT_TrueTest()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/fake");
            Link<TestRecord> operand2 = new Link<TestRecord>("http://example.com/fake");

            Assert.True(operand1 == operand2);
        }

        [Fact]
        public void opEquality_LinkT_LinkT_FalseTest()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/foo");
            Link<TestRecord> operand2 = new Link<TestRecord>("http://example.com/bar");

            Assert.False(operand1 == operand2);
        }

        [Fact]
        public void opEquality_LinkT_LinkT_SameTest()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/fake");
            Link<TestRecord> operand2 = operand1;

            Assert.True(operand1 == operand2);
        }

        [Fact]
        public void opEquality_LinkTNull_LinkT()
        {
            Link<TestRecord> operand1 = null;
            Link<TestRecord> operand2 = new Link<TestRecord>("http://example.com/fake");

            Assert.False(operand1 == operand2);
        }

        [Fact]
        public void opEquality_LinkT_LinkTNull()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/fake");
            Link<TestRecord> operand2 = null;

            Assert.False(operand1 == operand2);
        }

        [Fact]
        public void opInequality_LinkT_LinkT_TrueTest()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/foo");
            Link<TestRecord> operand2 = new Link<TestRecord>("http://example.com/bar");

            Assert.True(operand1 != operand2);
        }

        [Fact]
        public void opInequality_LinkT_LinkT_FalseTest()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/fake");
            Link<TestRecord> operand2 = new Link<TestRecord>("http://example.com/fake");

            Assert.False(operand1 != operand2);
        }

        [Fact]
        public void opInequality_LinkT_LinkT_SameTest()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/fake");
            Link<TestRecord> operand2 = operand1;

            Assert.False(operand1 != operand2);
        }

        [Fact]
        public void opInequality_LinkTNull_LinkT()
        {
            Link<TestRecord> operand1 = null;
            Link<TestRecord> operand2 = new Link<TestRecord>("http://example.com/fake");

            Assert.True(operand1 != operand2);
        }

        [Fact]
        public void opInequality_LinkT_LinkTNull()
        {
            Link<TestRecord> operand1 = new Link<TestRecord>("http://example.com/fake");
            Link<TestRecord> operand2 = null;

            Assert.True(operand1 != operand2);
        }

        [Fact]
        public void opImplicit_T_LinkTNull()
        {
            Link<TestRecord> obj = null;
            TestRecord actual = obj;

            Assert.Null(actual);
        }

        [Fact]
        public void opImplicit_T_LinkT()
        {
            FakeRepository.Clear();

            try
            {
                string urn = "urn://test/" + Token.New();

                FakeRepository.Register<TestRecord>(new { urn = urn, xml = "<test />" });

                Link<TestRecord> obj = new Link<TestRecord>();

                TestRecord expected = new TestRecord
                {
                    Urn = urn
                };

                obj.Set(expected);

                TestRecord actual = obj;

                Assert.Equal<AbsoluteUri>(expected.Urn, actual.Urn);
            }
            finally
            {
                FakeRepository.Reset();
            }
        }

        [Fact]
        public void opImplicit_LinkT_TNull()
        {
            TestRecord obj = null;
            Link<TestRecord> actual = obj;

            Assert.Null(actual);
        }

        [Fact]
        public void opImplicit_LinkT_T()
        {
            TestRecord expected = new TestRecord
            {
                Urn = "urn://test/" + Token.New()
            };

            Link<TestRecord> actual = expected;

            Assert.Equal<string>(expected.Urn, actual.Urn);
        }

        [Fact]
        public void op_Set_Tnull()
        {
            Assert.Throws<ArgumentNullException>(() => new Link<TestRecord>().Set(null));
        }

        [Fact]
        public void op_Set_T()
        {
            TestRecord expected = new TestRecord
            {
                Urn = "urn://test/" + Token.New()
            };

            Link<TestRecord> actual = new Link<TestRecord>();
            actual.Set(expected);

            Assert.Equal<AbsoluteUri>(expected.Urn, actual.Urn);
        }

        [Fact]
        public void op_Equals_object()
        {
            Link<TestRecord> obj = new Link<TestRecord>("http://example.com/fake");
            Link<TestRecord> comparand = new Link<TestRecord>("http://example.com/fake");

            Assert.True(obj.Equals(comparand as object));
        }

        [Fact]
        public void op_Equals_objectDiffer()
        {
            Link<TestRecord> obj = new Link<TestRecord>("http://example.com/foo");
            Link<TestRecord> comparand = new Link<TestRecord>("http://example.com/bar");

            Assert.False(obj.Equals(comparand as object));
        }

        [Fact]
        public void op_Equals_objectSame()
        {
            Link<TestRecord> obj = new Link<TestRecord>("http://example.com/fake");

            Assert.True(obj.Equals(obj as object));
        }

        [Fact]
        public void op_Equals_objectNull()
        {
            Assert.False(new Link<TestRecord>().Equals(null as object));
        }

        [Fact]
        public void op_Equals_objectString()
        {
            Assert.False(new Link<TestRecord>().Equals("test" as object));
        }

        [Fact]
        public void op_GetHashCode()
        {
            Link<TestRecord> obj = new Link<TestRecord>("http://example.com/test");

            int expected = obj.ToString().GetHashCode();
            int actual = obj.GetHashCode();

            Assert.Equal<int>(expected, actual);
        }

        [Fact]
        public void op_ToString()
        {
            string expected = "<link urn=\"urn://example/test\" />";

            Link<TestRecord> obj = new Link<TestRecord>();
            obj.Set(new TestRecord
            {
                Urn = "urn://example/test"
            });

            string actual = obj.ToString();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void IXmlSerializable_op_GetSchema()
        {
            Assert.Throws<NotSupportedException>(() => (new Link<TestRecord>() as IXmlSerializable).GetSchema());
        }

        [Fact]
        public void IXmlSerializable_op_ReadXml_XmlReaderNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new Link<TestRecord>() as IXmlSerializable).ReadXml(null as XmlReader));
        }

        [Fact]
        public void IXmlSerializable_op_WriteXml_XmlWriterNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new Link<TestRecord>() as IXmlSerializable).WriteXml(null as XmlWriter));
        }
    }
}